package net.helix.engine.timeline.layer;

import java.util.ArrayList;

import net.helix.core.transform.Transform;
import net.helix.core.transform.TransformDimension;
import net.helix.core.transform.ViewTransform;
import net.helix.element.Element;
import net.helix.element.Element_Camera;
import net.helix.element.ViewNodeElement;
import net.helix.engine.timeline.Timeline;
import net.helix.engine.timeline.frame.TimelineFrame;
import net.helix.engine.timeline.frame.TimelineKeyFrame;
/**
 * It's confusing to explain how the 2d-3d camera layers work but here's a summary:<br>
 * You must add a layer as a child of a 2d or 3d camera layer in order to have the
 * contents of the layer have it's position transformed by that camera.
 * A 2d camera layer must have 2d layers as it's children, and likewise, a 3d
 * camera layer needs 3d layers as it's children. The View will contain a list
 * of all of the camera layers in the scene along with an option to show all views
 * compiled together called "Preview" (like a preview of the resulting image that
 * goes into the final movie). You can only change your editing viewport if you
 * are viewing a certain camera layer. In that camera layer, you can also move the
 * camera and tween it along the timeline.
 * @author Runouw
 */
public class TimelineLayer {
	/**
	 * Determines what type of layer this is, default is NORMAL,
	 * but layers can also be MASK type or GUIDE type. There should only be 1
	 * CAMERA type layer which controls the position of the 3d camera.
	 * A 2d layer can only be added to a 2d camera layer, and a 3d layer can only be
	 * added to a 3d camera layer.<br><br>
	 */
	public TimelineLayerType type;
	/**
	 * Determines if the layer is rendered in 2d or 3d coordinates.
	 * A 2d layer should force all it's children to contain 2d coordinates.
	 * 2d layers cannot contains 3d meshes or any other 3d element.
	 */
	public TransformDimension dimension;
	/**
	 * The name of the layer. Can be renamed in the timeline UI. There is no problem
	 * with 2 layers having the same name?
	 */
	public String name;
	/**
	 * List of frames. Even empty frames will have to be added to this list.
	 * Frames can be added in any location of the list. Except a non-keyFrame should
	 * not exist as the first index.
	 */
	public ArrayList<TimelineFrame> frames = null;
	/**
	 * If this layer is a folder or a mask or a camera, then it can contain children
	 * layers.
	 */
	public ArrayList<TimelineLayer> children = null;
	/**
	 * 
	 */
	public boolean showChildren = true;
	/**
	 * A reference to the timeline that this layer is contained in.
	 */
	private Timeline timeline;
	/**
	 * The constructor to create a new timeline layer.
	 * @param fill If true will automatically populate with an empty key frame.
	 * If this is false, you will have to set your own frames to it and don't
	 * forget to set the name!
	 */
	public TimelineLayer(Timeline timeline, boolean fill, String name, TimelineLayerType type, TransformDimension dimension){
		this.timeline = timeline;
		this.name = name;
		this.type = type;
		this.dimension = dimension;
		
		if(type != TimelineLayerType.FOLDER){
			frames = new ArrayList<TimelineFrame>();
		}
		if(type == TimelineLayerType.FOLDER || type == TimelineLayerType.CAMERA){
			children = new ArrayList<TimelineLayer>();
		}
		
		if(fill){
			int l = (int)(Math.random()*200);
			for(int i=0;i<l;i++){
				addNewFrame();
			}
			
		}
	}
	public void addFrame(TimelineFrame frame){
		frames.add(frame);
	}
	public void addFrame(int to, TimelineFrame frame){
		frames.add(to, frame);
	}
	public void addNewFrame(){
		if(frames.size() == 0){
			addFrame(new TimelineKeyFrame());
		}else{
			addFrame(new TimelineFrame());
		}
	}
	public void AddNewFrame(int to){
		addFrame(to, new TimelineKeyFrame());
	}
	public void AddChildLayer(TimelineLayer layer){
		children.add(0, layer);
	}
	/**
	 * Recursively checks all children of this layer for a match.
	 * @param layer - the child layer to check.
	 * @return a boolean if the child is found.
	 */
	public boolean containsChild(TimelineLayer layer){
		if(type != TimelineLayerType.FOLDER && type != TimelineLayerType.CAMERA)
			return false;
		
		for(int i=0;i<children.size();i++){
			TimelineLayer checkLayer = children.get(i);
			
			if(checkLayer == layer || checkLayer.containsChild(layer)){
				return true;
			}
			
			
		}
		return false;
	}
	public void removeChildLayer(TimelineLayer layer){
		children.remove(layer);
	}
	public void addLayerBefore(TimelineLayer layer, TimelineLayer before){
		for(int i=0;i<children.size();i++){
			TimelineLayer checkLayer = children.get(i);
			if(checkLayer == before){
				children.add(i, layer);
				return;
			}
		}
		throw new Error("Error: addLayerBefore, was not in list");
	}
	public void addLayerAfter(TimelineLayer layer, TimelineLayer after){
		for(int i=0;i<children.size();i++){
			TimelineLayer checkLayer = children.get(i);
			if(checkLayer == after){
				children.add(i+1, layer);
				return;
			}
		}
		throw new Error("Error: addLayerAfter, was not in list");
	}
	/**
	 * Renders the frame specified at this layer and also calls the same function on all child layers.
	 */
	public void renderAtFrame(float frame){
		if(frames != null){
			int intFrame = (int) frame;
			
			if(frames.size() >= 0 && intFrame < frames.size()){
				TimelineFrame theFrame = frames.get(intFrame);
				theFrame.render();
			}
			
		}
		if(children != null){
			for(int i=children.size()-1;i>=0;i--){
				children.get(i).renderAtFrame(frame);
			}
			
		}
	}
	
	public Timeline getTimeline(){
		return timeline;
	}
	public ViewNodeElement getAsElement(float frame){
		// TODO: get the camera's position to set the transformation
		
		/*
		if(dimension == TransformDimension.TWO){
			// newElement.setTransformFlat();
		}else{
			//newElement.setTransformFlat();
		}
		*/
		
		ViewTransform viewTransform = new ViewTransform();
		
		Transform transform = new Transform(dimension);
		
		if(dimension == TransformDimension.THREE){
			// TODO: make default camera starting position
			//transform.setScale(1f/100f, 1f/100f, 1f/100f);
			transform.translate(-1000f, -500f, -1000f);
			transform.setRotate(-45f, 10f, 0);
			
		}else{
		}
		
		viewTransform.setTransform(transform);
		
		Element_Camera newElement = new Element_Camera(getTimeline(), this, frame, viewTransform);
		
		return newElement;
		
	}
	
	
	// TODO make it have a method to display error messages like "Can't place this in a 2d layer..." ect.
	public void addElement(Element element, float frame){
		System.out.println("adding element to frame "+frame + " in layer "+name);
		TimelineFrame theFrame = frames.get((int) frame);
		if(theFrame != null){
			if(theFrame instanceof TimelineKeyFrame){
				((TimelineKeyFrame) theFrame).AddElement(element, dimension);
			}else{
				// TODO: get closest keyFrame to the left, then add to that.
			}
		}else{
			System.out.println("frame " + frame + " is null");
			
		}
		
		
		
	}
	
	
}
